package com.xd.common.util;

import java.util.Random;
import java.util.function.Supplier;

public class RetryUtil {

    /**
     * 带重试机制的执行
     */
    public static <T> T executeWithRetry(Supplier<T> supplier, int maxRetries,
                                         RetryStrategy strategy) {
        Random random = new Random();

        for (int i = 0; i < maxRetries; i++) {
            try {
                T result = supplier.get();
                if (isSuccess(result)) {
                    return result;
                }
                System.out.println("执行失败，准备第 " + (i + 1) + " 次重试");
            } catch (Exception e) {
                System.out.println("执行异常，准备第 " + (i + 1) + " 次重试: " + e.getMessage());
            }

            if (i < maxRetries - 1) {
                long waitTime = strategy.calculateWaitTime(i, random);
                sleep(waitTime);
            }
        }
        throw new RuntimeException("重试 " + maxRetries + " 次后仍然失败");
    }

    private static <T> boolean isSuccess(T result) {
        // 根据实际业务判断成功条件
        if (result instanceof String) {
            return "OK".equals(result);
        }
        return result != null;
    }

    private static void sleep(long milliseconds) {
        try {
            System.out.println("等待 " + milliseconds + "ms 后重试...");
            Thread.sleep(milliseconds);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("重试被中断", e);
        }
    }

    // 重试策略接口
    public interface RetryStrategy {
        long calculateWaitTime(int retryCount, Random random);
    }

    // 指数退避策略
    public static class ExponentialBackoffStrategy implements RetryStrategy {
        private final long initialDelay;//起始时间
        private final double multiplier;

        public ExponentialBackoffStrategy(long initialDelay, double multiplier) {
            this.initialDelay = initialDelay;
            this.multiplier = multiplier;
        }

        @Override
        public long calculateWaitTime(int retryCount, Random random) {
            return (long) (initialDelay * Math.pow(multiplier, retryCount));
        }
    }

}